Parameter guide

What is a parameter?

A parameter usually represents a single value of a single feature of an instrument, e.g. the frequency of a function generator, the mode of a multimeter (resistance, current, or voltage), or the input impedance of an oscilloscope channel. Basic attributes of a Parameter are:

  • name: the name used internally by QCoDeS, e.g. ‘input_impedance’

  • label: the label to use for plotting this parameter

  • unit: the physical unit. ALWAYS use SI units if a unit is applicable

  • set_cmd, the command to set the parameter. Either a SCPI string with a single ‘{}’, or a function taking one argument (see examples below)

  • get_cmd: the command to get the parameter. Follows the same scheme as set_cmd

Note that there are many more attributes, the full list can be found in the Parameter documentation.

A basic example of a parameter is microwave_source.frequency, in which case microwave_source is an Instrument, and the Parameter is frequency.
Getting and setting the microwave frequency can be performed as such:
microwave_source.frequency(42e9)  # Set microwave source frequency to 42 GHz
microwave_source.frequency()  # Get frequency
>>> 42e9

Note that these set/get commands do not simply set a variable in Python; usually they actually send VISA commands to the instrument to get/set the frequency. For examples, look at the QCoDeS drivers.

In QCoDes, measurements are performed by varying parameter values and measuring other parameters. This is one of the primary motivations for creating a Parameter: having a standardized way for setting/getting/saving data during a measurement.
This is both an advantage and disadvantage.
The advantage is that information about the Parameter being measured is known beforehand (e.g. name, unit, shape), and so does not need to be specified when creating a measurement. The disadvantage is that requiring everything measured to be a Parameter removes some flexibility, as one cannot simply run arbitrary Python code, though there are ways to (partially) circumvent this.

Note: The new QCoDeS DataSet has dropped the requirement of only sweeping and measuring Parameters (more specifically it does not rely on the Loop class). However, our group’s version of QCoDeS has not switched to this method.

Parameter types

There are three basic types of Parameters, categorized by the type of value it holds: - Parameter The Parameter is the most common type of Parameter.
It can hold a single value (e.g. int, float, bool, string). - ArrayParameter The ArrayParameter can hold an array of values. The array shape should be specified so that size of the DataArray can be determined during a measurement. - MultiParameter The MultiParameter can hold multiple values. Each of these values can be either a single value (like a Parameter), or an array (like an ArrayParameter).
In this notebook, we will focus on the standard Parameter.
For details on the ArrayParameter and MultiParameter see the docs/examples/Parameters.ipynb notebook

Instrument-independent parameters

Parameters need not be attached to Instruments; they can be created independent of them.
Here we see a simple example, where a Parameter simply holds a value:
[ ]:
from qcodes import Parameter
param = Parameter('param', set_cmd=None)  # Default set_cmd is False, in which case we cannot set it (see below)

param(42)  # set value
param()  # get value
42
Parameters have many features, the two most important of which are functions called during get/set.
The simplest method to do this is via the keyword arguments get_cmd and set_cmd passed during instantiation:
  • get_cmd: Function to call during get command. Must accept zero arguments, and return value. If attached to an Instrument, it can also be a string, which corresponds to the VISA command. Can also be

    • False: Raise an error when a get is called

    • None: Return latest set value (This is the default)

  • set_cmd: Function to call during set command Must accept one argument, which is the value being set If attached to an Instrument, it can also be a string, which corresponds to the VISA command. Can also be False and None (see above), the default being False.

As an example, here we create a parameter with a specific action during get and set: - During a get, it will return a random value between 0 and 1 - During a set, it will print the value it’s being set to.

[ ]:
import numpy as np
def print_value(value):
    print('Value set to', value)

p = Parameter('param', get_cmd=np.random.uniform, set_cmd=print_value)

p(42)  # Setting calls a print statement
p()  # Get return a random value between 0 and 1
Value set to 42
0.960839463495372
Parameters have many more features related to it’s get/set functionality, such as validation, ramping, value parsing/mapping, scaling, etc. These are all attributes of parameters and can also be changed after instantiation.
For a full list, look at the documentation of the Parameter and _BaseParameter.

Subclassing parameters

While the get_cmd/set_cmd are sufficient for most needs, more complex situations can arise where simple commands do not suffice. For example, the parameter is not passed as self, and so the get/set function cannot access other parameter attributes. In these cases, it is better to subclass the Parameter and add these functionalities in the subclass.

To demonstrate this, we create a parameter that either returns the latest set value, or the exponent of that value depending on the boolean attribute exponentiate:

[ ]:
class ExponentiateParameter(Parameter):
    exponentiate = False

    def get_raw(self):
        if self.exponentiate:
            return np.exp(self.raw_value)
        else:
            return self.raw_value
[ ]:
exponentiate_parameter = ExponentiateParameter('exponentiation', initial_value=3)

print('Without exponentiation:', exponentiate_parameter())

exponentiate_parameter.exponentiate = True
print('With exponentiation:', exponentiate_parameter())
Without exponentiation: 3
With exponentiation: 20.085536923187668

Subclassing Parameters can be a very useful way to create complex parameters with many functions (e.g. a retuning sequence).

Note: A third way to add get/set commands is through the ParameterNode (see below)

Parameter set callbacks

There are situations where we want an ancillary function to be called every time a parameter is set.
This is facilitated by connecting a callback to a parameter.
In this example, we have a parameter to set the magnetic field.
Our goal is to measure the temperature every time the magnetic field is changed.
We do this by attaching the function measure_temperature to the magnetic field parameter (via magnetic_field.connect)
[ ]:
def measure_temperature(value):
    print(f'Temperature is at {np.random.randint(0, 300)} K')

magnetic_field = Parameter('magnetic_field', set_cmd=None)
magnetic_field.connect(measure_temperature,
                       update=True)  # Update value to perform initial temperature_measurement

print('Setting magnetic field to 1T')
magnetic_field(1)

print('Setting magnetic field to 2T')
magnetic_field(2)
Temperature is at 53 K
Setting magnetic field to 1T
Temperature is at 241 K
Setting magnetic field to 2T
Temperature is at 54 K
Parameters can also be connected to each other. These can also have a scale and offset applied to the value.
Here we connect param2 to param1 with an offset of 2:
[ ]:
param1 = Parameter('param1', set_cmd=None)
param2 = Parameter('param2', set_cmd=None)
param1.connect(param2, offset=2, update=False)

param1(42)
param2()
44

Linking a parameter to config

Note: This feature is only available with SilQ, which uses the SubConfig.

The config is a dictionary containing general settings related to the experiment.
Parameters can not only be connected to other parameters, they can also be connected to a key in the config.
This has the advantage that every time the config is updated, the parameter will reflect this value.
[ ]:
from silq import config
config.properties.read_duration = 1
config
{'properties': {'read_duration': 1}}
[ ]:
read_duration = Parameter('read_duration', set_cmd=None, initial_value=0)
read_duration()
0
We now connect the parameter to the config path properties.read_duration.
Initially, this won’t change the value:
[ ]:
read_duration.set_config_link('config:properties.read_duration')  # Note that config paths start with `config:`
read_duration()
0

However, once we modify the value in the config, the parameter value does change:

[ ]:
config.properties.read_duration = 2
read_duration()
2